﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using ReBasic.Model;

namespace ReBasic.Database
{
    public class ProBase
    {
        static bool isInit = false;

        public static event MainFrame.TripleHandler ProcessEvent;
        public static event MainFrame.InfoHandler StateEvent;

        public static string ProbasePath = @"F:\Relationship_Extraction\";

        public static Item[] SupPool, SubPool;
        //Memory pool, index order for each sup/sub
        //The order is the index of each concept, no relevance with frequency.
        public static int[] SupFreqList, SubFreqList;
        //LIST STRUCTURE, Used in freq desc search, find next item's index
        public static int[] SupIdxStart, SubIdxStart;
        //Store start point for each sup/sub in pool, use in binary search
        public static int[] SupFreqStart, SubFreqStart;
        //LIST HEAD, Store head point for each sup/sub in pool, use in freq search
        public static int[] SupSum, SubSum;
        //Store Summation of Frequency for each sup/sub
        public static int[] SubCount;
        //Store Summation of Count for each sub
        public static Dictionary<string, int> SupConDict, SubConDict;         //Hash: string->int
        public static string[] SupUhash, SubUhash;                            //Uhash: int->string

        /// <summary>
        /// Generate Hashes.
        /// </summary>
        static void Init_CreateHash()
        {
            if (SupConDict != null) return;
            int cnt = 0, sub_cnt = 0, sup_cnt = 0;
            StreamReader sr;
            FileStream fsr;
            SupConDict = new Dictionary<string, int>();
            SubConDict = new Dictionary<string, int>();
            fsr = new FileStream(ProbasePath + "core47_5.tax", FileMode.Open);
            sr = new StreamReader(fsr, Encoding.UTF8);
            StateEvent("Creating Hash... Reading file...", false);
            while (!sr.EndOfStream)
            {
                string str = sr.ReadLine();
                string[] spt = str.Split('\t');
                if (!SupConDict.ContainsKey(spt[0]))
                    SupConDict.Add(spt[0], sup_cnt++);
                if (!SubConDict.ContainsKey(spt[1]))
                    SubConDict.Add(spt[1], sub_cnt++);
                if (++cnt % 100000 == 0)
                    ProcessEvent(MainFrame.TotalDbItems, cnt,
                        (int)(100.0 * cnt / MainFrame.TotalDbItems));
            }
            sr.Close();
            fsr.Close();
            StateEvent("Read complete, " + cnt.ToString() + " items.", true);
            SupUhash = new string[SupConDict.Count];
            foreach (KeyValuePair<string, int> kvp in SupConDict)
                SupUhash[kvp.Value] = kvp.Key;
            SubUhash = new string[SubConDict.Count];
            foreach (KeyValuePair<string, int> kvp in SubConDict)
                SubUhash[kvp.Value] = kvp.Key;
            StateEvent("Complete Creating Hash.", true);
        }

        static int Compare_SupPool(IndexRelation a, IndexRelation b)
        {
            if (a.sup_idx == b.sup_idx)
                return a.sub_idx.CompareTo(b.sub_idx);
            return a.sup_idx.CompareTo(b.sup_idx);
        }

        static int Compare_SubPool(IndexRelation a, IndexRelation b)
        {
            if (a.sub_idx == b.sub_idx)
                return a.sup_idx.CompareTo(b.sup_idx);
            return a.sub_idx.CompareTo(b.sub_idx);
        }

        /// <summary>
        /// Make the files used to load Probase
        /// </summary>
        static void PrePare_MakeFile()
        {
            Init_CreateHash();
            int cnt = 0;
            StreamReader sr;
            StreamWriter sw;
            FileStream fsr, fsw;
            fsr = new FileStream(ProbasePath + "core47_5.tax", FileMode.Open);
            sr = new StreamReader(fsr, Encoding.UTF8);
            StateEvent("Make File...", false);
            SupPool = new Item[MainFrame.TotalDbItems];
            List<IndexRelation> lst = new List<IndexRelation>();
            #region Read
            while (!sr.EndOfStream)
            {
                string str = sr.ReadLine();
                string[] spt = str.Split('\t');
                lst.Add(new IndexRelation(SupConDict[spt[0]], SubConDict[spt[1]], int.Parse(spt[2])));
                if (++cnt % 100000 == 0)
                    ProcessEvent(MainFrame.TotalDbItems, cnt,
                        (int)(100.0 * cnt / MainFrame.TotalDbItems));
            }
            sr.Close();
            fsr.Close();
            StateEvent("Read complete.", true);
            #endregion
            #region SUP FILE
            lst.Sort(new Comparison<IndexRelation>(Compare_SupPool));
            cnt = 0;
            fsw = new FileStream(ProbasePath + "core47_5_sup_order.tax", FileMode.Create);
            sw = new StreamWriter(fsw, Encoding.UTF8);
            for (cnt = 0; cnt < MainFrame.TotalDbItems; cnt++)
            {
                sw.WriteLine(lst[cnt].sup_idx.ToString() + "\t"
                    + lst[cnt].sub_idx.ToString() + "\t" + lst[cnt].freq.ToString());
                if (cnt % 100000 == 0)
                    ProcessEvent(MainFrame.TotalDbItems, cnt,
                        (int)(100.0 * cnt / MainFrame.TotalDbItems));
            }
            sw.Close();
            fsw.Close();
            StateEvent("Write Sup Complete.", true);
            #endregion
            #region SUB FILE
            lst.Sort(new Comparison<IndexRelation>(Compare_SubPool));
            cnt = 0;
            fsw = new FileStream(ProbasePath + "core47_5_sub_order.tax", FileMode.Create);
            sw = new StreamWriter(fsw, Encoding.UTF8);
            for (cnt = 0; cnt < MainFrame.TotalDbItems; cnt++)
            {
                sw.WriteLine(lst[cnt].sup_idx.ToString() + "\t"
                    + lst[cnt].sub_idx.ToString() + "\t" + lst[cnt].freq.ToString());
                if (cnt % 100000 == 0)
                    ProcessEvent(MainFrame.TotalDbItems, cnt,
                        (int)(100.0 * cnt / MainFrame.TotalDbItems));
            }
            sw.Close();
            fsw.Close();
            StateEvent("Write Sub Complete.", true);
            #endregion
        }

        static void Init_FillPool()
        {
            SupPool = new Item[MainFrame.TotalDbItems];
            SubPool = new Item[MainFrame.TotalDbItems];
            SupIdxStart = new int[MainFrame.TotalSupConcept + 1];
            SubIdxStart = new int[MainFrame.TotalSubConcept + 1];
            SupSum = new int[MainFrame.TotalSupConcept];
            SubSum = new int[MainFrame.TotalSubConcept];
            SubCount = new int[MainFrame.TotalSubConcept];
            StreamReader sr;
            FileStream fsr;
            int cnt, a = 0, b = 0, c = 0, last;
            #region SUP FILE
            fsr = new FileStream(ProbasePath + "core47_5_sup_order.tax", FileMode.Open);
            sr = new StreamReader(fsr, Encoding.UTF8);
            last = -1;
            cnt = 0;
            while (!sr.EndOfStream)
            {
                string str = sr.ReadLine();
                string[] spt = str.Split('\t');
                a = int.Parse(spt[0]);
                b = int.Parse(spt[1]);
                c = int.Parse(spt[2]);
                SupSum[a] += c;
                if (a != last)
                {
                    last = a;
                    SupIdxStart[a] = cnt;
                }
                SupPool[cnt] = new Item(b, c);
                if (++cnt % 100000 == 0)
                    ProcessEvent(MainFrame.TotalDbItems, cnt,
                        (int)(100.0 * cnt / MainFrame.TotalDbItems));
            }
            SupIdxStart[a + 1] = cnt;
            sr.Close();
            fsr.Close();
            StateEvent("Sup Pool Complete.", true);
            #endregion
            #region SUB FILE
            fsr = new FileStream(ProbasePath + "core47_5_sub_order.tax", FileMode.Open);
            sr = new StreamReader(fsr, Encoding.UTF8);
            last = -1;
            cnt = 0;
            while (!sr.EndOfStream)
            {
                string str = sr.ReadLine();
                string[] spt = str.Split('\t');
                a = int.Parse(spt[0]);
                b = int.Parse(spt[1]);
                c = int.Parse(spt[2]);
                SubSum[b] += c;
                SubCount[b]++;
                if (b != last)
                {
                    last = b;
                    SubIdxStart[b] = cnt;
                }
                SubPool[cnt] = new Item(a, c);
                if (++cnt % 100000 == 0)
                    ProcessEvent(MainFrame.TotalDbItems, cnt,
                        (int)(100.0 * cnt / MainFrame.TotalDbItems));
            }
            SubIdxStart[b + 1] = cnt;
            sr.Close();
            fsr.Close();
            StateEvent("Sub Pool Complete.", true);
            #endregion
        }

        static void Init_FillFreq()
        {
            SupFreqList = new int[MainFrame.TotalDbItems];
            SubFreqList = new int[MainFrame.TotalDbItems];
            SupFreqStart = new int[MainFrame.TotalSupConcept];
            SubFreqStart = new int[MainFrame.TotalSubConcept];
            int cnt, len, stp;
            List<Item> tmpSort;
            #region SUP FILE
            for (cnt = 0; cnt < MainFrame.TotalSupConcept; cnt++)
            {
                stp = SupIdxStart[cnt];
                len = SupIdxStart[cnt + 1] - SupIdxStart[cnt];
                tmpSort = new List<Item>();
                for (int i = 0; i < len; i++)
                    tmpSort.Add(new Item(stp + i, SupPool[stp + i].freq));
                tmpSort.Sort();
                len = tmpSort.Count;
                SupFreqStart[cnt] = tmpSort[0].idx;
                for (int i = 1; i < len; i++)
                    SupFreqList[tmpSort[i - 1].idx] = tmpSort[i].idx;
                SupFreqList[tmpSort[len - 1].idx] = -1;
            }
            StateEvent("Sup Frequency Complete.", true);
            #endregion
            #region SUB FILE
            for (cnt = 0; cnt < MainFrame.TotalSubConcept; cnt++)
            {
                stp = SubIdxStart[cnt];
                len = SubIdxStart[cnt + 1] - SubIdxStart[cnt];
                tmpSort = new List<Item>();
                for (int i = 0; i < len; i++)
                    tmpSort.Add(new Item(stp + i, SubPool[stp + i].freq));
                tmpSort.Sort();
                len = tmpSort.Count;
                SubFreqStart[cnt] = tmpSort[0].idx;
                for (int i = 1; i < len; i++)
                    SubFreqList[tmpSort[i - 1].idx] = tmpSort[i].idx;
                SubFreqList[tmpSort[len - 1].idx] = -1;
            }
            StateEvent("Sub Frequency Complete.", true);
            #endregion
        }

        public static void GenerateProbase()
        {
            if (isInit)
                return;
            Init_CreateHash();
            Init_FillPool();
            Init_FillFreq();
            isInit = true;
            GC.Collect();
            StateEvent("--- Generate Probase Complete. ---", true);
        }
    }

    public struct Item : IComparable<Item>
    {
        public int idx;
        public int freq;

        public Item(int ix, int f) { idx = ix; freq = f; }

        #region IComparable<Item> 成员

        public int CompareTo(Item other)
        {
            return -(freq.CompareTo(other.freq));
        }

        #endregion
    }

}
